﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Security;
using System.Configuration.Provider;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Web.Configuration;
using System.Configuration;
using WebPattern.Core.Validators;
using WebPattern.DataAccess;

namespace WebPattern.Core.Security
{
    public sealed class WebPatternMembershipProvider : MembershipProvider
    {
        #region Data Members
        private int newPasswordLength = 8;
        private MachineKeySection machineKey;
        private bool _writeExceptionsToEventLog;

        // for overriden properties
        private string _appName;
        private bool _enablePasswordReset;
        private bool _enablePasswordRetrieval;
        private int _maxInvalidPasswordAttempts;
        private int _minRequiredNonAlphanumericCharacters;
        private int _minRequiredPasswordLength;
        private int _passwordAttemptWindow;
        private bool _requiresQuestionAndAnswer;
        private bool _requiresUniqueEmail;
        private MembershipPasswordFormat _passwordFormat;
        private string _passwordStrengthRegularExpression;
        private UserRepository _userRepository;
        #endregion

        #region Overriden Properties
        public UserRepository UserRpstr 
        {
            get { return new UserRepository();}
        }
        public override string ApplicationName { get { return _appName; } set { _appName = value; } }
        public override bool EnablePasswordReset { get { return _enablePasswordReset; } }
        public override bool EnablePasswordRetrieval { get { return _enablePasswordRetrieval; } }
        public override int MaxInvalidPasswordAttempts { get { return _maxInvalidPasswordAttempts; } }
        public override int MinRequiredNonAlphanumericCharacters { get { return _minRequiredNonAlphanumericCharacters; } }
        public override int MinRequiredPasswordLength { get { return _minRequiredPasswordLength; } }
        public override int PasswordAttemptWindow { get { return _passwordAttemptWindow; } }
        public override bool RequiresQuestionAndAnswer { get { return _requiresQuestionAndAnswer; } }
        public override bool RequiresUniqueEmail { get { return _requiresUniqueEmail; } }
        public override MembershipPasswordFormat PasswordFormat { get { return _passwordFormat; } }
        public override string PasswordStrengthRegularExpression { get { return _passwordStrengthRegularExpression; } }
        #endregion
       
        #region Not required for override methods
        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            //
            // Initialize values from web.config.
            //

            if (config == null)
                throw new ArgumentNullException("config");

            if (name == null || name.Length == 0)
                name = "OdbcMembershipProvider";

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Sample ODBC Membership provider");
            }

            // Initialize the abstract base class.
            base.Initialize(name, config);

            _appName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            _maxInvalidPasswordAttempts = Convert.ToInt32(GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
            _passwordAttemptWindow = Convert.ToInt32(GetConfigValue(config["passwordAttemptWindow"], "10"));
            _minRequiredNonAlphanumericCharacters = Convert.ToInt32(GetConfigValue(config["minRequiredNonAlphanumericCharacters"], "1"));
            _minRequiredPasswordLength = Convert.ToInt32(GetConfigValue(config["minRequiredPasswordLength"], "7"));
            _passwordStrengthRegularExpression = Convert.ToString(GetConfigValue(config["passwordStrengthRegularExpression"], ""));
            _enablePasswordReset = Convert.ToBoolean(GetConfigValue(config["enablePasswordReset"], "true"));
            _enablePasswordRetrieval = Convert.ToBoolean(GetConfigValue(config["enablePasswordRetrieval"], "true"));
            _requiresQuestionAndAnswer = Convert.ToBoolean(GetConfigValue(config["requiresQuestionAndAnswer"], "false"));
            _requiresUniqueEmail = Convert.ToBoolean(GetConfigValue(config["requiresUniqueEmail"], "true"));
            _writeExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "true"));

            string temp_format = config["passwordFormat"];
            if (temp_format == null)
            {
                temp_format = "Hashed";
            }

            switch (temp_format)
            {
                case "Hashed":
                    _passwordFormat = MembershipPasswordFormat.Hashed;
                    break;
                case "Encrypted":
                    _passwordFormat = MembershipPasswordFormat.Encrypted;
                    break;
                case "Clear":
                    _passwordFormat = MembershipPasswordFormat.Clear;
                    break;
                default:
                    throw new ProviderException("Password format not supported.");
            }


            // Get encryption and decryption key information from the configuration.
            Configuration cfg =
              WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            machineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");

            if (machineKey.ValidationKey.Contains("AutoGenerate"))
                if (PasswordFormat != MembershipPasswordFormat.Clear)
                    throw new ProviderException("Hashed or Encrypted passwords " +
                                                "are not supported with auto-generated keys.");

            //base.Initialize(name, config);         
        }
        public override string Name
        {
            get { return base.Name; }
        }
        public override string Description
        {
            get { return base.Description; }
        }
        protected override void OnValidatingPassword(ValidatePasswordEventArgs e)
        {
            base.OnValidatingPassword(e);
        }
        protected override byte[] EncryptPassword(byte[] password, System.Web.Configuration.MembershipPasswordCompatibilityMode legacyPasswordCompatibilityMode)
        {
            return base.EncryptPassword(password, legacyPasswordCompatibilityMode);
        }
        protected override byte[] DecryptPassword(byte[] encodedPassword)
        {
            return base.DecryptPassword(encodedPassword);
        }
        protected override byte[] EncryptPassword(byte[] password)
        {
            return base.EncryptPassword(password);
        }
        #endregion

        #region Overriden Methods
        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            Debug.WriteLine("CreateUser(...)");

            try
            {
                ValidateNewPasswordFormat(username, password);
            }
            catch (Exception)
            {
                status = MembershipCreateStatus.InvalidPassword;
                return null;
            }

            if (DuplicatesEmailAddress(email))
            {
                status = MembershipCreateStatus.DuplicateEmail;
                return null;
            }

            MembershipUser membershipUser = GetUser(username, false);
            if (membershipUser == null)
            {

                DateTime createDate = DateTime.Now;
                User _user = new User()
                {
                    CreatedAt = createDate,
                    UserName = username,
                    ApplicationName = this.ApplicationName,
                    Email = email,
                    Password = EncodePassword(password), // SecurityHelper.PasswordEncrypt(password, username),
                    PasswordQuestion = passwordQuestion,
                    PasswordAnswer = String.IsNullOrWhiteSpace(passwordAnswer) ? null : EncodePassword(passwordAnswer),    //SecurityHelper.PasswordEncrypt(passwordAnswer, username),
                    IsApproved = isApproved,
                    LastPasswordChangedDate = createDate,
                    LastActivityDate = createDate,
                    IsLockedOut = false,
                    LastLockoutDate = createDate,
                    FailedPasswordAttemptCount = 0,
                    FailedPasswordAttemptWindowStart = createDate,
                    FailedPasswordAnswerAttemptCount = 0,
                    FailedPasswordAnswerAttemptWindowStart = createDate
                };

                try
                {
                    UserRpstr.Save(_user);
                    UserRpstr.Commit();
                    status = MembershipCreateStatus.Success;
                }
                catch (Exception ex)
                {
                    throw new MemberAccessException("Error processing membership data", ex);
                }

                return GetUser(username, false);
            }
            else
            {
                status = MembershipCreateStatus.DuplicateEmail;
            }

            return null;
        }

        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            Debug.WriteLine("DeleteUser(...)");
            
            try
            {
                User _user = UserRpstr.GetUserByName(username, this.ApplicationName);

                if (_user != null)
                {
                    UserRpstr.Delete(_user);
                    UserRpstr.Commit();
                }
                else
                    return false; //TO DO: MembershipCreateStatus ->   throw new MemberAccessException("The supplied user name is not found.");
                
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }

            return true;
        }

        public override void UpdateUser(MembershipUser user)
        {
            Debug.WriteLine("UpdateUser(...)"); 
            
            try
            {
                User _user = UserRpstr.GetById(Convert.ToInt64(user.ProviderUserKey));
               
                if (_user != null)
                {
                    _user.Email = user.Email;
                    _user.Comment = user.Comment;
                    _user.IsApproved = user.IsApproved;
                    UserRpstr.Update(_user);
                    UserRpstr.Commit();
                }
                //else
                //  TO DO: MembershipCreateStatus ->   throw new MemberAccessException("The supplied user name is not found.");
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Unable to retrieve user data", ex);
            }
        }

        public override bool ValidateUser(string username, string password)
        {
            Debug.WriteLine("ValidateUser(...)");

            bool isValid = false;
            
            try
            {
                User _user = UserRpstr.GetUserByName(username, this.ApplicationName);

                if (_user == null)
                    return false;

                if (CheckPassword(password, _user.Password))
                {
                    if (_user.IsApproved)
                    {
                        isValid = true;
                        _user.PrevLoginDate = _user.LastLoginDate;
                        _user.LastLoginDate = DateTime.Now;
                    }
                }
                else
                {
                    _user.FailedPasswordAttemptCount++;
                }


                UserRpstr.Update(_user);
                UserRpstr.Commit();
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Unable to retrieve user data", ex);
            }

            return isValid;
        }

        public override bool UnlockUser(string userName)
        {
            Debug.WriteLine("UnlockUser(...)");
            
            try
            {
                User _user = UserRpstr.GetUserByName(userName, this.ApplicationName);

                if (_user == null)
                    return false;

                _user.IsLockedOut = false;
                _user.LastLockoutDate = DateTime.Now;

                UserRpstr.Update(_user);
                UserRpstr.Commit();
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Unable to retrieve user data", ex);
            }

            return true;
        }

        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            Debug.WriteLine("GetUser(...)");

            try
            {
                User _user = UserRpstr.GetUserByName(username, this.ApplicationName);

                if (_user == null)
                    return null;

                if (userIsOnline)
                {
                    _user.LastActivityDate = DateTime.Now;

                    UserRpstr.Update(_user);
                    UserRpstr.Commit();
                }

                return MapUser(_user);
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Unable to retrieve user data", ex);
            }
        }

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            Debug.WriteLine("GetUser(...)");

            try
            {
                User _user = UserRpstr.GetById(Convert.ToInt64(providerUserKey));

                if (_user == null)
                    return null;

                if (userIsOnline)
                {
                    _user.LastActivityDate = DateTime.Now;

                    UserRpstr.Update(_user);
                    UserRpstr.Commit();
                }

                return MapUser(_user);

            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Unable to retrieve user data", ex);
            }
        }

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            Debug.WriteLine("GetAllUsers(...)");
            
            try
            {
                IList<User> _userList = UserRpstr.GetAllUsers(pageIndex, pageSize, ApplicationName);
                totalRecords = _userList.Count;
                return MapUsers(_userList);
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }
        }

        public override int GetNumberOfUsersOnline()
        {
            Debug.WriteLine("GetNumberOfUsersOnline(...)");
            
            DateTime compareTime = DateTime.Now.AddMinutes(-Membership.UserIsOnlineTimeWindow);

            try
            {
                return UserRpstr.GetNumberOfUsersOnline(compareTime, this.ApplicationName);
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }
        }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            Debug.WriteLine("FindUsersByEmail(...)");
            
            try
            {
                IList<User> _userList = UserRpstr.FindUsersByEmail(emailToMatch, pageIndex, pageSize, this.ApplicationName);
                totalRecords = _userList.Count;
                return MapUsers(_userList);
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }
        }

        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            Debug.WriteLine("FindUsersByName(...)");
            
            try
            {
                IList<User> _userList = UserRpstr.FindUsersByName(usernameToMatch, pageIndex, pageSize, ApplicationName);
                totalRecords = _userList.Count;
                return MapUsers(_userList);
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }
        }

        public override string GetUserNameByEmail(string email)
        {
            Debug.WriteLine("GetUserNameByEmail(...)");
            
            try
            {
                return UserRpstr.GetUsernameByEmail(email, this.ApplicationName);
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Unable to retrieve user data", ex);
            }
        }

        /// <summary>
        /// Gets the password for the specified user name from the data source. Throw exception if have logical error.
        /// </summary>
        /// <param name="username">The user to retrieve the password for.</param>
        /// <param name="answer">The password answer for the user.</param>
        /// <returns>The password for the specified user name.</returns>
        /// <remarks></remarks>
        public override string GetPassword(string username, string answer)
        {
            Debug.WriteLine("GetPassword(...)");
            
            if (!EnablePasswordRetrieval)
                throw new ProviderException("Password Retrieval Not Enabled.");

            if (PasswordFormat == MembershipPasswordFormat.Hashed)
                throw new ProviderException("Cannot retrieve Hashed passwords.");            

            return RetrievePasswordFromDatabase(username, answer);
        }

        /// <summary>
        /// Resets a user's password to a new, automatically generated password. Throw exception if have logical error.
        /// </summary>
        /// <param name="username">The user to reset the password for.</param>
        /// <param name="answer">The password answer for the specified user.</param>
        /// <returns>The new password for the specified user.</returns>
        /// <remarks></remarks>
        public override string ResetPassword(string username, string answer)
        {
            Debug.WriteLine("ResetPassword(...)");

            if (!EnablePasswordReset)
                throw new NotSupportedException("Password reset is not enabled.");

            if (string.IsNullOrEmpty(answer) && RequiresQuestionAndAnswer)
            {
                UpdateFailureCount(username, "passwordAnswer");
                throw new ProviderException("Password answer required for password reset.");
            }

            string newPassword = System.Web.Security.Membership.GeneratePassword(newPasswordLength, MinRequiredNonAlphanumericCharacters);
            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);
            OnValidatingPassword(args);

            if (args.Cancel)
                if (args.FailureInformation != null)
                    throw args.FailureInformation;
                else
                    throw new MembershipPasswordException("Reset password canceled due to password validation failure.");

            User _user;

            try
            {
                _user = UserRpstr.GetUserByName(username, this.ApplicationName);
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }


            if (_user == null)
                throw new MembershipPasswordException("The supplied user name is not found. Password not Reset.");

            if (_user.IsLockedOut)
                throw new MembershipPasswordException("The supplied user is locked out. Password not Reset.");

            if (RequiresQuestionAndAnswer && !CheckPassword(answer, _user.PasswordAnswer))
            {
                UpdateFailureCount(username, "passwordAnswer");
                throw new MembershipPasswordException("Incorrect password answer.");
            }

            try
            {
                _user.Password = EncodePassword(newPassword);
                _user.LastPasswordChangedDate = DateTime.Now;

                UserRpstr.Update(_user);
                UserRpstr.Commit();
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }

            return newPassword;
        }

        /// <summary>
        /// Processes a request to update the password for a membership user. Throw exception if have logical error.
        /// </summary>
        /// <param name="username">The user to update the password for.</param>
        /// <param name="oldPassword">The current password for the specified user.</param>
        /// <param name="newPassword">The new password for the specified user.</param>
        /// <returns>true if the password was updated successfully; otherwise, false.</returns>
        /// <remarks></remarks>
        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            Debug.WriteLine("ChangePassword(...)");
            
            if (!ValidateUser(username, oldPassword))
                return false;

            ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);
            OnValidatingPassword(args);

            if (args.Cancel)
                if (args.FailureInformation != null)
                    throw args.FailureInformation;
                else
                    throw new MembershipPasswordException("Change password canceled due to new password validation failure.");
            
            try
            {
                User _user = UserRpstr.GetUserByName(username, ApplicationName);

                if (_user == null)
                    return false;

                _user.Password = EncodePassword(newPassword);
                _user.LastPasswordChangedDate = DateTime.Now;

                UserRpstr.Update(_user);
                UserRpstr.Commit();
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }

            return true;
        }

        /// <summary>
        /// Processes a request to update the password question and answer for a membership user. Throw exception if have logical error.
        /// </summary>
        /// <param name="username">The user to change the password question and answer for.</param>
        /// <param name="password">The password for the specified user.</param>
        /// <param name="newPasswordQuestion">The new password question for the specified user.</param>
        /// <param name="newPasswordAnswer">The new password answer for the specified user.</param>
        /// <returns>true if the password question and answer are updated successfully; otherwise, false.</returns>
        /// <remarks></remarks>
        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            Debug.WriteLine("ChangePasswordQuestionAndAnswer(...)");
            
            if (!ValidateUser(username, password))
                return false;

            try
            {
                User _user = UserRpstr.GetUserByName(username, ApplicationName);

                if (_user == null)
                    return false;

                _user.PasswordQuestion = newPasswordQuestion;
                _user.PasswordAnswer = EncodePassword(newPasswordAnswer);

                UserRpstr.Update(_user);
                UserRpstr.Commit();
            }

            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }

            return true;
        }
        #endregion

        #region Helper Methods
        /// <summary>
        /// A helper function to retrieve config values from the configuration file..
        /// </summary>
        /// <param name="configValue">The config value.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return configValue;
        }

        /// <summary>
        /// Encrypts, Hashes, or leaves the password clear based on the PasswordFormat.
        /// </summary>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private string EncodePassword(string password)
        {
            string encodedPassword = password;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    encodedPassword =
                      Convert.ToBase64String(EncryptPassword(Encoding.Unicode.GetBytes(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    HMACSHA1 hash = new HMACSHA1();
                    hash.Key = HexToByte(machineKey.ValidationKey);
                    encodedPassword =
                      Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password)));
                    break;
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return encodedPassword;
        }

        /// <summary>
        /// Uns the encode password. Decrypts or leaves the password clear based on the PasswordFormat.
        /// </summary>
        /// <param name="encodedPassword">The encoded password.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private string UnEncodePassword(string encodedPassword)
        {
            string password = encodedPassword;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Clear:
                    break;
                case MembershipPasswordFormat.Encrypted:
                    password =
                      Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password)));
                    break;
                case MembershipPasswordFormat.Hashed:
                    throw new ProviderException("Cannot unencode a hashed password.");
                default:
                    throw new ProviderException("Unsupported password format.");
            }

            return password;
        }

        /// <summary>
        /// Converts a hexadecimal string to a byte array. Used to convert encryption
        /// </summary>
        /// <param name="hexString">key values from the configuration.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private byte[] HexToByte(string hexString)
        {
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }

        /// <summary>
        /// Compares password values based on the MembershipPasswordFormat.
        /// </summary>
        /// <param name="password">The password.</param>
        /// <param name="dbpassword">The dbpassword.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private bool CheckPassword(string password, string dbpassword)
        {
            string pass1 = password;
            string pass2 = dbpassword;

            switch (PasswordFormat)
            {
                case MembershipPasswordFormat.Encrypted:
                    pass2 = UnEncodePassword(dbpassword);
                    break;
                case MembershipPasswordFormat.Hashed:
                    pass1 = EncodePassword(password);
                    break;
                default:
                    break;
            }

            if (pass1 == pass2)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        ///  A helper method that performs the checks and updates associated with password failure tracking.
        /// </summary>
        /// <param name="username">The username.</param>
        /// <param name="failureType">Type of the failure.</param>
        /// <remarks></remarks>
        private void UpdateFailureCount(string username, string failureType)
        {
            try
            {
                User _user = UserRpstr.GetUserByName(username, this.ApplicationName);

                if (failureType == "password")
                    _user.FailedPasswordAttemptCount++;
                else if (failureType == "passwordAnswer")
                    _user.FailedPasswordAnswerAttemptCount++;

                UserRpstr.Update(_user);
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }
            
            if (failureType == "password")
               failureType = "password"; //TO DO : MembershipCreateStatus ->  throw new MembershipPasswordException("Incorrect password.");
            else if (failureType == "passwordAnswer")
                failureType = "passwordAnswer"; //TO DO : MembershipCreateStatus -> throw new MembershipPasswordException("Incorrect password answer.");
            else 
                throw new MembershipPasswordException("Unknown failure type.");  
        }

        private void ValidateNewPasswordFormat(string username, string newPassword)
        {
            var args = new ValidatePasswordEventArgs(username, newPassword, false);
            args.FailureInformation = new MembershipPasswordException("Change password canceled due to new password validation failure.");
            OnValidatingPassword(args);
            
            if (args.Cancel)
            {
                throw args.FailureInformation;
            }
        }
        private bool DuplicatesEmailAddress(string email)
        {
            var dupEmail = false;
            if (this.RequiresUniqueEmail)
            {
                if (!string.IsNullOrEmpty(GetUserNameByEmail(email)))
                {
                    dupEmail = true;
                }
            }
            return dupEmail;
        }

        private MembershipUser MapUser(User usr)
        {
            return new MembershipUser(
                providerName: this.Name,
                name: usr.UserName,
                providerUserKey: usr.Id,
                email: usr.Email,
                passwordQuestion: usr.PasswordQuestion,
                comment: usr.Comment,
                isApproved: usr.IsApproved,
                isLockedOut: usr.IsLockedOut,
                creationDate: usr.CreatedAt,
                lastLoginDate: usr.LastLoginDate.GetValueOrDefault(),
                lastActivityDate: usr.LastLogOutDate.GetValueOrDefault(),
                lastPasswordChangedDate: usr.LastPasswordChangedDate.GetValueOrDefault(),
                lastLockoutDate: usr.LastLogOutDate.GetValueOrDefault());
        }
        private MembershipUserCollection MapUsers(IEnumerable<User> users)
        {
            MembershipUserCollection results = new MembershipUserCollection();

            foreach (User val in users)
                results.Add(MapUser(val));

            return results;
        }

        private string RetrievePasswordFromDatabase(string username, string answerGiven)
        {
            string password;
            string passwordAnswer;

            User _user;
            try
            {
                _user = UserRpstr.GetUserByName(username, this.ApplicationName);
            }
            catch (Exception ex)
            {
                throw new MemberAccessException("Error processing membership data", ex);
            }

            if (_user == null)
                throw new MembershipPasswordException("The supplied user name is not found.");

            if (_user.IsLockedOut)
                throw new MembershipPasswordException("The supplied user is locked out.");

            password = _user.Password;
            passwordAnswer = _user.PasswordAnswer;

            if (RequiresQuestionAndAnswer && !CheckPassword(answerGiven, passwordAnswer))
            { 
                UpdateFailureCount(username, "passwordAnswer");
                throw new MembershipPasswordException("Incorrect password answer.");
            }

            if (PasswordFormat == MembershipPasswordFormat.Encrypted)
                password = UnEncodePassword(password); // SecurityHelper.PasswordDecrypt(username, password);

            return password;
        }
        #endregion
    }
}
